package 使用日志;

class Logback和SLF4J {

}
SLF4J和Logback
SLF4J概念
简单日志门面，对应的英文为Simple Logging Facade，是存取日志的标准接口。

也就是说它仅仅是一个日志输出的接口，并不是一种具体的实现方案，就像 JDBC一样只是统一的接口。

想要使用必须搭配其他的日志实现方案，如：log4j，logback ，JDK logging 等等。

可以看到 Logback 直接继承自 SLF4J，它比其它所有的日志系统更快并且更小，包含了许多独特并且有用的特性。

Logback概述
Logback 分成三个不同的模块，logback-core，logback-classic，logback-access 。

快速使用
添加依赖：

logback-classic 模块需要在 classpath 添加 slf4j-api.jar、logback-core.jar 以及 logback-classic.jar。

maven项目中，使用坐标：

<dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
</dependency>

该依赖会包含其他几个坐标，可以手动添加相同版本的依赖，也可以只添加这一个。



如果你安装了 Maven，你可以在 logback 的解压文件夹中运行 mvn install 来构建 logback 以及它所包含的模块。Maven 会自动下载 logback 需要的其它类库。

测试例子：

public class Test {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("Test");
        logger.debug("hello world,this is a debug message...");
      
      	// 打印内部的状态
        LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
        StatusPrinter.print(loggerContext);
    }
}

控制台打印结果：

12:08:39.804 [main] DEBUG com.codesy.test.Test1 - hello world,this is a debug message…

12:08:39,639 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
12:08:39,640 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
12:08:39,640 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
12:08:39,656 |-INFO in ch.qos.logback.classic.BasicConfigurator@1af6ecc - Setting up default configuration.

Logger 和 LoggerFactory 是 SLF4J包中的类，LoggerContext 获得内部状态信息。

得到 Logger 实例，通过它调用不同的方法 trace() ,debug()，info()，warn()，error()来向指定的位置输出日志信息。

控制台显示的信息位没有找到配置文件 [logback-test.xml] [logback.groovy] [logback.xml]，从而 Setting up default configuration（设置默认配置）

我们可以自己创建对应的配置文件，让日志信息采取我们想要的方式打印存储。也可以使用默认简单的配置，即不手写配置文件，使用默认配置
类 ch.qos.logback.classic.BasicConfigurator，该类只有一个方法，里面设置了默认的配置方案。

//public class BasicConfigurator extends ContextAwareBase implements Configurator {

   public BasicConfigurator() {}

   public ExecutionStatus configure(LoggerContext lc) {
       addInfo("Setting up default configuration.");

       ConsoleAppender<ILoggingEvent> ca = new ConsoleAppender<ILoggingEvent>();
       ca.setContext(lc);
       ca.setName("console");
       LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<ILoggingEvent>();
       encoder.setContext(lc);

       // same as
       // PatternLayout layout = new PatternLayout();
       // layout.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
       // %msg%n");
       TTLLLayout layout = new TTLLLayout();

       layout.setContext(lc);
       layout.start();
       encoder.setLayout(layout);

       ca.setEncoder(encoder);
       ca.start();

       Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
       rootLogger.addAppender(ca);

       // let the caller decide
       return ExecutionStatus.NEUTRAL;
   }
}


基本的Logback组件
Logback 的构建重点是三个主要的类上：Logger，Appender 和 Layouts。

组件Logger
每一个 Logger 实例都会依附在 LoggerContext 上，它管理所有 Logger实例，并且通过一个树状的层级结构来管理。

如果一个 Logger 实例的名字 A 加上一个 . 和另一个 Logger 实例名字 B ，那么 A 就是B的祖先，如果它们之间再无其他 Logger，A就是B的父级。

例如 一个Logger实例名为 com.codesy 如果 另一个实例名为 com.codesy.test ，那么前者就是后者的父级。名称是大小写敏感的。

示例：获取一个 名为 com.codesy.test 的Logger实例，查看它所依附的 LoggerContext 的所有 Logger实例

public class Test {

    public static void main(String[] args) {
		//获取 com.codesy.test 实例
        Logger logger= LoggerFactory.getLogger("com.codesy.test");
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
		//getLoggerList()方法返回所有Logger实例
        List<ch.qos.logback.classic.Logger> loggerList = context.getLoggerList();
        for (ch.qos.logback.classic.Logger logger1 : loggerList) {
            System.out.println(logger1.getName());
        }
    }
}

输出结果：

ROOT
com
com.codesy
com.codesy.test

可以看到LoggerContext 中一共存在4个Logger实例，以 . 进行分割。获得 com.codesy.test 实例，其实同时得到了com，com.codesy实例，它们都是com.codesy.test的前缀，通过 . 相连。

还注意到一个最顶级的Logger实例 ROOT ，它是这棵树的最高层。

Logger的等级

Logger 实例被分成不同的 等级，不同的等级定义在 ch.qos.logback.classic.Level 类中。

等级分类有 （TRACE < DEBUG < INFO < WARN < ERROR）

每一个获取到的 Logger实例如果没有指定等级，那么它会继承离它最近的一个祖先的等级。

ROOT 实例默认等级为 DEBUG ，倘若其他Logger实例都没有指定等级，那么继承ROOT的DEBUG等级。

如果实例本身指定了等级，那么就直接用自身的等级，不用继承。

示例：
loger名字     指定层级       有效层级
root         debug        debug
x            info         info
x.y          none         info
x.y.z        error        error


Logger的规则

每一个 Logger 实例都会有日志输出方法，例如：
trace()，debug()，info()，warn()，error() 等等。

每一条打印语句都表示这条日志的打印等级：debug("...") 表示这个日志等级为 DEBUG。

Logger 实例有等级，通过调用的打印方法也有等级，那么它们之间有什么关系呢？

如果一条的日志的打印等级大于等于 Logger实例 的等级，该条日志才可以被打印出来。

各等级的排序为：TRACE < DEBUG < INFO < WARN < ERROR。

示例：

public class Test {

    public static void main(String[] args) {
        Logger logger= LoggerFactory.getLogger("com.codesy.test");
        logger.debug("这是一条DEBUG等级的日志消息");
        logger.trace("这是一条TRACE等级的日志消息");
    }
}

控制台打印：

16:37:41.598 [main] DEBUG com.codesy.test - 这是一条DEBUG等级的日志消息

可以看到 logger.trace(“这是一条TRACE等级的日志消息”); 这条语句没有被输出到控制台，因为 logger 没有指定等级默认继承 ROOT 的等级 DEBUG。这条日志消息的等级为 TRACE 比 DEBUG小，无法打印。

注意：

对于我们上述的 Logger 实例它是 org.slf4j.Logger 包下的，我们也可以获取 ch.qos.logback.classic.Logger 包下的 Logger实例。

public final class Logger implements org.slf4j.Logger	//实现了org.slf4j.Logger包下的Logger

该 Logger实例包含特有的方法，如设置Logger实例等级等

logger.setLevel(TRACE);		//这样该Logger实例等级为 TRACE

另一点：

获取 Logger实例 通过 ：

Logger logger = LoggerFactory.getLogger("com.codesy.test")；

还有一种通过类的Class对象获得Logger实例，推荐使用 "com.codesy.test"这种形式，这个命名策略能够看出日志的来源是哪里。

组件Appender与Layout
上述的Logger等级规则可以使得低等级的日志不被打印出来，那么 Appender就是日志输出的位置，包console、file、remote socket server、MySQL、PostgreSQL、Oracle 或者其它的数据库、JMS、remote UNIX Syslog daemons 等位置。

一个Logger实例可以有多个 appender （多个输出位置）。

子级Logger实例会继承来自父级的 appender，例如 com.codesy 这个实例添加了 一个 console appender，那么 com.codesy.test 除了它本身的appender外 ，也会拥有console appender。当然前提是 additivity = true 这个属性设置为 true表示可以继承父级的appender，为 false则不能，默认为 true。

Layout 它使用户可以自定义日志的输出格式。

参数化日志
在以前的log4j时候，没有实现slf4j日志接口，打印语句是这样的

int num = 1;
logger.debug("The num is " + num + ".");

这样会带来效率成本，它会拼接字符串，并且需要把num转成String类型，消耗性能。

而在实现slf4j日志接口的logback里，这样打印会节省性能

int num = 1;
logger.debug("num is {}.",num);

将 ‘{}’ 替换成 num的字符串形式,{}是占位符。

它也支持对象数组形式的参数，如

Object[] array = {1,"Hello",true};
logger.debug("Integer is {}, String is {}, Boolean is {}.",array);
